package com.why.cachecloud.client.redis.cachecloud.client.lettuce.util;

import com.why.cachecloud.client.redis.cachecloud.client.lettuce.util.util.codec.DefaultCodec;
import com.why.cachecloud.client.redis.cachecloud.client.lettuce.util.util.RedisUtil;
import com.why.cachecloud.client.redis.cachecloud.client.lettuce.util.util.impl.LettuceClusterUtil;
import com.why.cachecloud.client.redis.cachecloud.client.lettuce.util.util.impl.LettuceSentinelUtil;
import io.lettuce.core.*;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.time.Duration;

/**
 * @Description
 * @Author why
 * @Version 1.0.0
 * @Since 1.0
 * @Date 2022/8/30
 */
@Configuration
public class LettuceConfig {

    @Bean
    public ClientResources.Builder clientResourcesBuilder() {
        return DefaultClientResources.builder()
                .ioThreadPoolSize(8)
                .computationThreadPoolSize(10);
    }

    @Bean
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "cluster")
    public ClusterClientOptions.Builder clusterClientOptionsBuilder() {
        SocketOptions socketOptions = SocketOptions.builder().keepAlive(true).tcpNoDelay(false)
                .connectTimeout(Duration.ofSeconds(5)).build();

        ClusterClientOptions.Builder clientOptionsBuilder = ClusterClientOptions.builder()
                .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(5)))
                .socketOptions(socketOptions);

        return clientOptionsBuilder;
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "cluster")
    @DependsOn("cacheCloudConstants")
    public RedisClusterClient redisClusterClient(@Value("${why.components.cachecloud.appId}") long appId,
                                                 @Value("${why.components.cachecloud.password}") String password,
                                                 ClientResources.Builder clientResourcesBuilder,
                                                 ClusterClientOptions.Builder clusterClientOptionsBuilder) {

        RedisClusterClient redisClusterClient = LettuceClientBuilder
                .redisCluster(appId, password)
                .setClientResourcesBuilder(clientResourcesBuilder)
                .setClusterClientOptionsBuilder(clusterClientOptionsBuilder)
                .build();

        return redisClusterClient;
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "cluster")
    public <T> StatefulRedisClusterConnection<String, T> clusterConnection(RedisClusterClient redisClusterClient,
                                                                           DefaultCodec<T> defaultCodec) {
        StatefulRedisClusterConnection<String, T> connection = redisClusterClient.connect(defaultCodec);
        connection.setReadFrom(ReadFrom.REPLICA_PREFERRED);
        return connection;
    }


    @Bean
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "sentinel")
    public ClientOptions.Builder sentinelClientOptionsBuilder() {
        SocketOptions socketOptions = SocketOptions.builder().keepAlive(true).tcpNoDelay(false)
                .connectTimeout(Duration.ofSeconds(5)).build();

        ClientOptions.Builder optionsBuilder = ClusterClientOptions.builder()
                .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(5)))
                .socketOptions(socketOptions);

        return optionsBuilder;
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "sentinel")
    @DependsOn("cacheCloudConstants")
    public RedisClient redisSentinelClient(@Value("${why.components.cachecloud.appId}") long appId,
                                           @Value("${why.components.cachecloud.password}") String password,
                                           ClientResources.Builder clientResourcesBuilder,
                                           ClientOptions.Builder sentinelClientOptionsBuilder) {

        RedisClient redisClient = LettuceClientBuilder
                .redisSentinel(appId, password)
                .setClientResourcesBuilder(clientResourcesBuilder)
                .setClientOptionsBuilder(sentinelClientOptionsBuilder)
                .build();

        return redisClient;
    }

    @Bean
    public <T> DefaultCodec<T> defaultCodec() {
        return new DefaultCodec<T>();
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "sentinel")
    public <T> StatefulRedisConnection sentinelConnection(RedisClient redisSentinelClient,
                                                          DefaultCodec<T> defaultCodec) {
        StatefulRedisConnection<String, T> connection = redisSentinelClient.connect(defaultCodec);
        return connection;
    }

    @Bean
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "cluster")
    public RedisUtil lettuceClusterUtil(StatefulRedisClusterConnection clusterConnection) {
        LettuceClusterUtil lettuceClusterUtil = new LettuceClusterUtil();
        lettuceClusterUtil.setClusterConnection(clusterConnection);
        return lettuceClusterUtil;
    }

    @Bean
    @ConditionalOnProperty(name = "why.components.cachecloud.model", havingValue = "sentinel")
    public RedisUtil lettuceSentinelUtil(StatefulRedisConnection statefulRedisConnection) {
        LettuceSentinelUtil lettuceSentinelUtil = new LettuceSentinelUtil();
        lettuceSentinelUtil.setConnection(statefulRedisConnection);
        return lettuceSentinelUtil;
    }
}
